package com.study.singleton;


/**
 * 懒汉式单例模式
 */
public class LazySingletonTest {
    public static void main(String[] args) {

    }
}

class LazySingleton{

    //new不是一个原子性操作,加上volatile是为了防止指令重排序来保证可见性(每次该属性一更新立马就写会共享内存中(堆)，且其他线程在用的时候都会被通知得从内存中(堆)拿最新的数据来用) 底层是通过内存屏障解决,是操作系统底层来控制的
    private volatile static LazySingleton instance;

    //为什么需要写一个私有的构造方法？ 因为我们要做单例模式 创建对象得由该类自己来控制，这样做是为了防止不能给其他类在外面 new 本类
    private LazySingleton(){

    }

    // DCL双重检查锁
    public static LazySingleton getInstance(){

        //外层的判断是为了提高效率【不必synchronized等待】，里层的判断就是第一次实例化需要
        if (null == instance){  //为了提高效率，不用每次都阻塞去等前面的线程，去判断是不是为null
            synchronized(LazySingleton.class){ //同步
                if(null == instance){  // 确保单例
                    //jvm类加载过程详解基本上分为以下三步 显然不是原子性的 因为JIT(即时编译器)会在不影响结果的前提下为了提高效率可能会对指令进行重排
                    //所以如果不禁止指令重排,那么以下指令可能因为被重排序不按顺序执行,假如被重排序为 1 3 2 执行方式
                    //那么其他线程在 1.开辟空间执行完后 就能发现堆中的已经不是null了就会直接得到该对象 但此时就获取到未正确初始化完成的对象了,就会导致其他奇怪的问题
                    //当禁止指令重排后,那么必然按照1 2 3 步骤顺序来执行 则其他线程能拿到instance的时候肯定就是已经初始化完成的实例了
                    // 1. 开辟空间
                    // 2. 初始化空间数据
                    // 3. 赋值给栈的instance局部变量
                    instance = new LazySingleton();
                }
            }
        }

        return instance;
    }

}
